/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package sasp_udp;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidParameterSpecException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import sasp_KML.KMLgen;
import sasp_messages.CommCipher;

/**
 *
 * @author mauriello
 */
public class UDPTransceiver implements Runnable {
    private int portNum;
    private ArrayList<UDPListener> udpListeners;
    private Thread udpThread;
    private String ipAddress;
    private KMLgen K;
    private SecretKey sessionKey;
    private CommCipher aesCipher;

    public final static int DEFAULT_PORT    = 12345;
    public final static int MAX_PACKET_SIZE = 65507;

    public UDPTransceiver( ) throws IOException {
        this( DEFAULT_PORT );
    }

    public UDPTransceiver( int _portNum ) throws IOException {
        try {
            udpThread = new Thread(this);
            udpListeners = new ArrayList<UDPListener>();
            portNum = _portNum;
            udpListeners.clear();
            InetAddress addr = InetAddress.getLocalHost();
            ipAddress = addr.getHostAddress();
            startUdpThread();
        } catch (UnknownHostException ex) {
            Logger.getLogger(UDPTransceiver.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void initCipher() throws IOException, NoSuchAlgorithmException, NoSuchPaddingException    {
        sessionKey = new SecretKeySpec(new sun.misc.BASE64Decoder().decodeBuffer("66mqpSztTMpBz3AJk6aJvw=="), "AES");
        aesCipher = new CommCipher(sessionKey);
    }
    public boolean addUdpListener( UDPListener _l ) {
        return udpListeners.add(_l);
    }

    public void addKML(KMLgen _K){
        K = _K;
    }
    public boolean removeUdpListener( UDPListener _l ) {
        return udpListeners.remove(_l);
    }

    public boolean transmitUdpMessage( String _hostname, int _port, String _txData ) {
        boolean retVal = true;
        byte[] encryptedTxData = null;

        //Encrypts the String _txData to be sent over the network using the AES Cipher
        try {
            encryptedTxData = aesCipher.encrypt(_txData.getBytes());
        } catch (InvalidKeyException ex) {
            Logger.getLogger(UDPTransceiver.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IllegalBlockSizeException ex) {
            Logger.getLogger(UDPTransceiver.class.getName()).log(Level.SEVERE, null, ex);
        } catch (BadPaddingException ex) {
            Logger.getLogger(UDPTransceiver.class.getName()).log(Level.SEVERE, null, ex);
        } catch (InvalidParameterSpecException ex) {
            Logger.getLogger(UDPTransceiver.class.getName()).log(Level.SEVERE, null, ex);
        }
        try {
            InetAddress    server = InetAddress.getByName(_hostname);
            DatagramSocket socket = new DatagramSocket();
            DatagramPacket packet = new DatagramPacket(encryptedTxData, encryptedTxData.length, server, _port);
            socket.send(packet);
        } catch (Exception ex) {
            retVal = false;
            Logger.getLogger(UDPTransceiver.class.getName()).log(Level.SEVERE, null, ex);
        }

        return retVal;
    }
    
    public int getPortNumber( ) {
        return portNum;
    }

    public String getIpAddress( ) {
        return ipAddress;
    }

    private void startUdpThread( ) {
        if( !udpThread.isAlive() ) {
            saspmanager.SASPManagerApp.addLine("UDP - Starting Thread @ Port: " + portNum);
            udpThread.start();
        }
    }

    private synchronized void handleRcvData( UDPMessage _inData ) {
        Iterator<UDPListener> udpIter = udpListeners.iterator();
        while ( udpIter.hasNext() ){
            udpIter.next().handleMessage(_inData);
        }
    }

    public void run() {
        byte [] buffer = new byte[UDPTransceiver.MAX_PACKET_SIZE];
        try {
            DatagramSocket server = new DatagramSocket(portNum);
            DatagramPacket packet = new DatagramPacket(buffer, buffer.length);

            while (true) {  // DO FOREVER
                saspmanager.SASPManagerApp.addLine("UDP - Waiting for Packet");
                // Wait for packet from client
                server.receive(packet);
                byte[] cipherText = new byte[packet.getLength()];
                System.arraycopy(packet.getData(), 0, cipherText, 0, packet.getLength());

                //Decrypts the received message into a string using the AES Cipher
                String decryptedMsg = aesCipher.decrypt(cipherText);

                UDPMessage theMessage = new UDPMessage(packet.getAddress().getHostAddress(), decryptedMsg);
                saspmanager.SASPManagerApp.addLine("UDP - Rcv: " + theMessage.getTheMessage().trim());

                // Notify Listeners
                this.handleRcvData(theMessage);

                // Reset Packet Length for Next Packet
                packet.setLength(buffer.length);
                theMessage = null;

                // tell KMLgen to write
                K.write();
            }
        } catch (Exception ex) {
            Logger.getLogger(UDPTransceiver.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}

